Container的職責在於創建、配置與組裝bean,昨天我們學到了
如何使用@Value的用法、完全註解開發、泛型依賴注入
今日將進入新的領域AOP,今日我們先談談AOP是在什麼樣的場景出現的吧
看看我們怎麼為計算器加入log
public interface Calculator {
    int add(int i,int j);
    int sub(int i,int j);
    int mul(int i,int j);
    int div(int i,int j);
}
我們可能會用System.out.println,但我們會發現要改log的format要改四個地方@@
public class MyCalcultor implements Calculator{
    @Override
    public int add(int i, int j) {
        System.out.println("add method start,parameter:"+i +","+j);
        int result = i+j;
        System.out.println("add method end,result:"+result);
        return result;
    }
    @Override
    public int sub(int i, int j) {
        System.out.println("sub method start,parameter:"+i +","+j);
        int result = i-j;
        System.out.println("sub method end,result:"+result);
        return result;
    }
    @Override
    public int mul(int i, int j) {
        System.out.println("mul method start,parameter:"+i +","+j);
        int result = i*j;
        System.out.println("mul method end,result:"+result);
        return result;
    }
    @Override
    public int div(int i, int j) {
        System.out.println("div method start,parameter:"+i +","+j);
        int result = i/j;
        System.out.println("div method end,result:"+result);
        return result;
    }
}
我們可能會想到寫一個LogUtils來取代System.out.println出現的位置
public class LogUtils {
    public static void logStart(String methodName,Object...args){
        System.out.println(methodName+" method start,parameter:"+ Arrays.asList(args));
    }
    public static void logEnd(String methodName,Object result){
        System.out.println(methodName+" method end,result:"+ result);
    }
}
然後你可能還會想要在方法的前後加入開始與結束時間,看一下效能
public class MyCalcultor implements Calculator{
    @Override
    public int add(int i, int j) {
        LogUtils.logStart("add",i,j);
        long startTime = System.currentTimeMillis();
        int result = i+j;
        LogUtils.logEnd("add",result);
        long endTime = System.currentTimeMillis();
        System.out.println("add method execute:"+(endTime - startTime));
        return result;
    }
    @Override
    public int sub(int i, int j) {
        LogUtils.logStart("add",i,j);
        long startTime = System.currentTimeMillis();
        int result = i-j;
        LogUtils.logEnd("add",result);
        return result;
    }
    @Override
    public int mul(int i, int j) {
        LogUtils.logStart("add",i,j);
        long startTime = System.currentTimeMillis();
        int result = i*j;
        LogUtils.logEnd("add",result);
        long endTime = System.currentTimeMillis();
        System.out.println("add method execute:"+(endTime - startTime));
        return result;
    }
    @Override
    public int div(int i, int j) {
        LogUtils.logStart("add",i,j);
        long startTime = System.currentTimeMillis();
        int result = i/j;
        LogUtils.logEnd("add",result);
        long endTime = System.currentTimeMillis();
        System.out.println("add method execute:"+(endTime - startTime));
        return result;
    }
}
然後...是不是可以再增加稽核軌跡看一下是那個user執行這個作業。然後你就會發現程式都糊再一起,真正的商業邏輯都被淹沒了。那有沒有方法能夠只保留商業邏輯像面這樣呢,而且還能保留log、稽核軌跡、效能紀錄呢,答案是可以的,就透過AOP來達成。
public class MyCalcultor implements Calculator{
    @Override
    public int add(int i, int j) {
        int result = i+j;
        return result;
    }
    @Override
    public int sub(int i, int j) {
        int result = i-j;
        return result;
    }
    @Override
    public int mul(int i, int j) {
        int result = i*j;
        return result;
    }
    @Override
    public int div(int i, int j) {
        int result = i/j;
        return result;
    }
}
面向切面編程,指在程式運行期間,將某段代碼動態的切入到指定的方法跟指定位置進行運作。面向能夠模組化我們關注的點,例如transaction management
要能達到將代碼動態切入到指定方法跟位置執行,需要透過動態代理的方式達成。透過代理對象在invoke對象方法前後就可以輕易加入想要執行的動作。
圖片來源:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-proxying
AOP底層的動態代理有兩種方式
JDK動態代理範例
public class CalculatorProxy {
    public static Calculator getProxy(final Calculator calculator){
        InvocationHandler invokHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                LogUtils.logStart(method.getName(),args);
                Object result = method.invoke(calculator,args);
                LogUtils.logEnd(method.getName(),result);
                return result;
            }
        };
        Class<?>[] interfaces = calculator.getClass().getInterfaces();
        ClassLoader loder =calculator.getClass().getClassLoader();
        Object proxy = Proxy.newProxyInstance(loder,interfaces,invokHandler);
        return (Calculator)proxy;
    }
}
@Test
public void testDay17(){
    Calculator proxy = CalculatorProxy.getProxy(new MyCalcultor());
    proxy.add(1,5);
}
Result